using BootstrapBlazorApp.Server.LLMs.api;
using BootstrapBlazorApp.Server.LLMs.chain;
using BootstrapBlazorApp.Server.LLMs.function.Kimi;
using BootstrapBlazorApp.Server.LLMs.messages;
using BootstrapBlazorApp.Server.LLMs.utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Net.Http.Headers;
using System.Text;

namespace BootstrapBlazorApp.Server.LLMs.model.Kimi;

public class ChatKimi(KimiConfig config):IBaseModel
{
    private List<KimiToolInput>? _functions;
    private Type? _toolType;
    
    public void BindTools(Type type)
    {
        _functions = ToolParserUtil.ParseFunction(type);
        _toolType = type;
    }
    
    private async Task<KimiOutput?> ToolCall(ChainContext input, JsonSerializerSettings settings,KimiOutput outPut)
    {
        var choice= outPut.GetChoices().FirstOrDefault();
        if (choice?.FinishReason!="tool_calls" || _toolType == null)
        {
            return outPut;
        }

        string inputContent;
        string toolOutPut;
        string response;
        ReturnMessage returnMessage = choice.Message;
        while (choice?.FinishReason == "tool_calls" && choice.Message.ToolCalls!=null)
        {
            input.Add(new AiMessage(returnMessage.ToolCalls));
            foreach (var toolCall in choice.Message.ToolCalls)
            {
                try
                {
                    toolOutPut = ToolParserUtil.ExecuteKimiFunction(_toolType, returnMessage)?.ToString() ?? string.Empty;

                    input.Add(new ToolMessage(toolOutPut, toolCall.Function.Name)
                    {
                         ToolCallId = toolCall.Id
                    });
                }
                catch (Exception ex)
                {
                    // 处理异常
                    Console.WriteLine($"Error executing tool function: {ex.Message}");
                }
            }
            inputContent= BuildKImiInput(input);
            response = await SendPostRequestAsync(config.BaseUrl, inputContent, config.ApiKey);
            outPut = JsonConvert.DeserializeObject<KimiOutput>(response, settings) ?? throw new InvalidOperationException();
            choice = outPut.GetChoices().FirstOrDefault();
        }
        
        return outPut;
    }
    
    private string BuildKImiInput(ChainContext input, bool isStream = false)
    {
        //设置初始参数
        KimiInput kimiInput = new KimiInput { Model = config.Model, Temperature = config.Temperature, TopP = config.TopP };

        if (isStream)
        {
            kimiInput.Stream = true;
        }
        

        foreach (var baseMessage in input.MessagesList)
        {
            //存入初始信息
            switch (baseMessage)
            {
                case ToolMessage toolMessage:
                    kimiInput.Messages.Add(new ReturnMessage
                    {
                        Role = toolMessage.Role, Content = toolMessage.Content, Name = toolMessage.Name,ToolCallId = toolMessage.ToolCallId
                    });
                    break;
                case AiMessage aiMessage:
                    kimiInput.Messages.Add(new ReturnMessage
                    {
                        Role = baseMessage.Role, Content = baseMessage.Content,ToolCalls = aiMessage.ToolCalls
                    });
                    break;
                default:
                    kimiInput.Messages.Add(new ReturnMessage
                    {
                        Role = baseMessage.Role, Content = baseMessage.Content
                    });
                    break;
            }
            
        }
        
        if (_functions != null)
        {
            kimiInput.Tools = _functions;
        }

        //序列化设置
        var setting = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
        return JsonConvert.SerializeObject(kimiInput, setting);
    }
    
    private static async Task<string> SendPostRequestAsync(string url, string jsonContent, string apiKey)
    {
        HttpClient httpClient = new HttpClient();
        //请求头，身份验证
        using var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
        //发送请求
        using HttpResponseMessage response=await httpClient.PostAsync(url, content);
        try
        {
            // 发送请求并获取响应
            response.EnsureSuccessStatusCode();
        }
        catch
        {
            await using var Stream = await response.Content.ReadAsStreamAsync();
            using var read = new StreamReader(Stream);
            Console.WriteLine(await read.ReadToEndAsync());
        }
        //获取响应内容
        await using var responseStream = await response.Content.ReadAsStreamAsync();
        using var reader = new StreamReader(responseStream);
        return await reader.ReadToEndAsync();
    }

    private async Task<ChainContext> ChatAsync(ChainContext input)
    {
        try
        {
            //保证属性名和键名一致
            var settings = new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new SnakeCaseNamingStrategy()
                }
            };
            //存入消息
            string kimiInput = BuildKImiInput(input);
            //获取响应内容
            string response=await SendPostRequestAsync(config.BaseUrl, kimiInput, config.ApiKey);
            //解析响应内容
            KimiOutput? outPut = JsonConvert.DeserializeObject<KimiOutput>(response,settings);
            //对是否有存在toolcall进行检查
            if (outPut != null)
            {
                outPut = await ToolCall(input, settings, outPut);
                //取出ai返回的最新消息(在工具类调用完之后的）
                string? messageContent = outPut?.Choices.Length > 0
                    ? outPut.Choices[outPut.Choices.Length - 1].Message.Content
                    : null;
                if (messageContent != null)
                {
                    input.Add(new AiMessage(messageContent));
                }
            }

            return input;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
    
    public async Task<ChainContext> InvokeAsync(string input)
    {
        ChainContext chainContext = new ChainContext();
        chainContext.Add(new HumanMessage(input));
        return await ChatAsync(chainContext);
    }
    
    public async Task<ChainContext> InvokeAsync(ChainContext input)
    {
        return await ChatAsync(input);
    }
    
    private static async IAsyncEnumerable<string?> SendPostRequestStreamAsync(string url, string jsonContent,
        string apiKey)
    {
        HttpClient httpClient = new HttpClient();
        //请求头，身份验证
        using var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
        //kimi的响应头中的 Content-Type 为 text/event-stream，告知服务器客户端可以接受 text/event-stream 类型的数据
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
        
        // 发送请求并获取响应
        using HttpResponseMessage response = await httpClient.PostAsync(url, content);
        response.EnsureSuccessStatusCode();
        await using var responseStream = await response.Content.ReadAsStreamAsync();
        using var reader = new StreamReader(responseStream, Encoding.UTF8); // 指定编码为UTF-8
        // 以数据块为单位读取
        string? currentDataBlock = null;
        //用data:[Done]标记结束所有数据块
        bool isDone = false;
        
        while (!isDone && (await reader.ReadLineAsync() is { } line))
        {
            line = line.Trim();

            if (line.StartsWith("data:"))
            {
                //从索引为6的开始提取
                string data = line.Substring(6).Trim();
                if (data == "[DONE]")
                {
                    isDone = true; //结束传输
                }
                else
                {
                    currentDataBlock = data;
                }
            }
            else if (!string.IsNullOrEmpty(line))
            {
                // 如果当前行不是空行，且不是以"data:"开头，那么它属于当前数据块的一部分，追加内容
                if (currentDataBlock != null)
                {
                    currentDataBlock += Environment.NewLine + line;
                }
            }

            //数据块非空或者遇到[DONE]
            if (currentDataBlock != null || isDone)
            {
                yield return currentDataBlock;
                currentDataBlock = null; // 重置当前数据块，
            }
        }
    }
    
    private async IAsyncEnumerable<ChainContext> ChatSteamAsync(ChainContext input)
    {
        string s = BuildKImiInput(input, true);
        input.Add(new AiMessage(""));
        //解析每个数据块,每次迭代接受一个
        await foreach (var response in SendPostRequestStreamAsync(config.BaseUrl, s, config.ApiKey))
        {
            if (response == null)
            {
                continue;
            }
            //序列化
            var settings = new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new SnakeCaseNamingStrategy()
                }
            };

            KimiOutput? outPut = JsonConvert.DeserializeObject<KimiOutput>(response,settings);
            
            if (outPut != null)
            {
                string messageContent = outPut.GetLastContent();
                input.IncrementalOutput = messageContent;
                input.MessagesList.Last().Content += messageContent;
                
            }
            yield return input;
            
            await Task.Delay(100);
        }
    }
    
    public async IAsyncEnumerable<ChainContext> InvokeStreamAsync(ChainContext input)
    {
        await foreach (var chunk in ChatSteamAsync(input))
        {
            yield return chunk;
        }
    }

    public async IAsyncEnumerable<ChainContext> InvokeStreamAsync(string input)
    {
        ChainContext chainContext = new ChainContext();
        chainContext.Add(new HumanMessage(input));
        await foreach (var chunk in ChatSteamAsync(chainContext))
        {
            yield return chunk;
        }
    }
}
